using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using ZombieHigh2.DeleD;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ZombieHigh2.World;
using ParticleSystem.Particles;

namespace ZombieHigh2.Parser
{
    /// <summary>
    /// This parser was originally designed to read DeleD XML files.
    /// It has been replaced with a newer version that reads my custom
    /// editor XML file format.
    /// </summary>
    public class DeledParser
    {
        const int ATTRIB_PRIM_ID = 0;
        const int ATTRIB_PRIM_NAME = 1;
        const int ATTRIB_PRIM_TYPE = 2;
        const int ATTRIB_PRIM_VIS = 3;
        const int ATTRIB_PRIM_SNAP = 4;
        const int ATTRIB_PRIM_AUTOUV = 5;
        const int ATTRIB_PRIM_GROUPID = 6;

        ArrayList materials;
        List<Primitive3D> primitiveList;
        Vector3 playerSpawnLocation;
        List<Vector3> zombieSpawnLocations;
        List<Trigger> triggers;
        List<WorldItem> items;
        List<StaticParticleEmitter> staticEmitters;

        public List<StaticParticleEmitter> StaticEmitters
        {
            get { return staticEmitters; }
        }

        public List<Primitive3D> Primitives
        {
            get { return primitiveList; }
        }

        public ArrayList Materials
        {
            get { return materials; }
        }

        public List<Trigger> Triggers
        {
            get { return triggers; }
        }

        public List<WorldItem> Items
        {
            get { return items; }
        }

        public Vector3 PlayerSpawn
        {
            get { return playerSpawnLocation; }
        }

        public List<Vector3> ZombieSpawns
        {
            get { return zombieSpawnLocations; }
        }

        public DeledParser()
        {
            materials = new ArrayList();
            primitiveList = new List<Primitive3D>();
            playerSpawnLocation = new Vector3();
            zombieSpawnLocations = new List<Vector3>();
            triggers = new List<Trigger>();
            items = new List<WorldItem>();
            staticEmitters = new List<StaticParticleEmitter>();
        }

        public void Debug_PrintPrimitiveList()
        {
            for (int p = 0; p < primitiveList.Count; p++)
            {
                ZombieHigh2.DeleD.Primitive3D primitive = (ZombieHigh2.DeleD.Primitive3D)primitiveList[p];

                foreach (TexturedPolygon poly in primitive.polygons)
                {
                    System.Console.WriteLine("Polygon");

                    for (int v = 0; v < poly.vertices.Count; v++)
                    {
                        Vertex vertex = (Vertex)poly.vertices[v];
                        TexturedVertex texturedCoords = (TexturedVertex)poly.textureCoords[v];

                        System.Console.Write("[" + vertex.x + ", " + vertex.y + ", " + vertex.z + "]");
                        System.Console.WriteLine("\t[" + texturedCoords.u0 + ", " + texturedCoords.v0 + "]");
                    }

                    System.Console.WriteLine("Converted Vertices");

                    VertexPositionNormalTexture[] array = poly.GetVertices();
                    foreach (VertexPositionNormalTexture vert in array)
                    {
                        System.Console.WriteLine("[" + vert.Position + "]" + "[" + vert.Normal + "]" + "[" + vert.TextureCoordinate + "]");
                    }
                }

            }
        }

        /// <summary>
        /// Reads the materials from the DeleD XML file.
        /// </summary>
        /// <param name="xmlDoc">The XML document to parse.</param>
        public void ReadMaterials(XmlDocument xmlDoc)
        {
            XmlNodeList matlist = xmlDoc.GetElementsByTagName("material");

            for (int i = 0; i < matlist.Count; i++)
            {
                Material cMaterial = new Material();
                XmlNode material = matlist[i];
                XmlAttributeCollection attributes = material.Attributes;
                XmlAttribute attribute = attributes[0];
                cMaterial.id = Int32.Parse(attribute.InnerText);

                XmlNodeList layers = material.ChildNodes;
                XmlNodeList textureDetails = layers[0].ChildNodes;
                XmlNode textureName = textureDetails[0];
                attributes = textureName.Attributes;
                attribute = attributes[0];
                cMaterial.texturename = attribute.InnerText;
                materials.Add(cMaterial);

                System.Console.Write("Material Id: " + cMaterial.id);
                System.Console.WriteLine("\tMaterial name is: " + cMaterial.texturename);
            }
        }

        public void ReadPrimitives(XmlDocument xmlDoc)
        {
            XmlNodeList primitives = xmlDoc.GetElementsByTagName("primitive");

            for (int i = 0; i < primitives.Count; i++)
            {
                ZombieHigh2.DeleD.Primitive3D cPrimitive = new ZombieHigh2.DeleD.Primitive3D();
                XmlNode primitiveNode = primitives[i];
                //Read the attributes
                String primitiveName = primitiveNode.Attributes[ATTRIB_PRIM_NAME].InnerText;
                cPrimitive.name = primitiveName;

                XmlNodeList tagVertsPolygons = primitiveNode.ChildNodes;
                XmlNode tagNode = tagVertsPolygons[0];
                cPrimitive.userInfo = tagNode.InnerText;

                XmlNode vertices = tagVertsPolygons[1];
                XmlNodeList vertList = vertices.ChildNodes;

                Vertex[] cVertices = new Vertex[vertList.Count];
                //System.Console.WriteLine("vertices " + vertList.Count);

                /*********************************************************
                 * Vertex Information
                 * *******************************************************/
                for (int v = 0; v < vertList.Count; v++)
                {
                    XmlNode vertex = vertList[v];
                    XmlAttributeCollection attributes = vertex.Attributes;
                    XmlAttribute id;

                    cVertices[v] = new Vertex();
                    //id
                    id = attributes[0];
                    //x
                    cVertices[v].x = Int32.Parse(attributes[1].InnerText);
                    //y
                    cVertices[v].y = Int32.Parse(attributes[2].InnerText);
                    //z
                    cVertices[v].z = Int32.Parse(attributes[3].InnerText);

                    //System.Console.WriteLine("Vertex " + id.InnerText + " [" + x.InnerText + ", " + y.InnerText + ", " + z.InnerText + "]");
                }

                /********************************************************
                 * Polygon Information
                 * ******************************************************/
                XmlNode polygons = tagVertsPolygons[2];
                XmlNodeList polyList = polygons.ChildNodes;

                for (int p = 0; p < polyList.Count; p++)
                {
                    TexturedPolygon texturedPoly = new TexturedPolygon();

                    XmlNode poly = polyList[p];
                    int matID = Int32.Parse(poly.Attributes[0].InnerText);
                    texturedPoly.materialID = matID;

                    XmlNodeList vIDs = poly.ChildNodes;
                    for (int vid = 0; vid < vIDs.Count; vid++)
                    {
                        //XmlAttribute vid, u, v;
                        int vertexID;
                        float u, v;

                        vertexID = Int32.Parse(vIDs[vid].Attributes[0].InnerText);
                        u = float.Parse(vIDs[vid].Attributes[1].InnerText);
                        v = float.Parse(vIDs[vid].Attributes[2].InnerText);

                        texturedPoly.AddTexturedVertex(cVertices[vertexID], u, v);
                        //System.Console.WriteLine("Polygon added: " + vertexID + " u: " + u + " v: " + v);
                    }

                    texturedPoly.CalculateBoundingBox();
                    cPrimitive.AddPolygon(texturedPoly);
                }

                cPrimitive.CalculateBoundingBox();
                primitiveList.Add(cPrimitive);
            }
        }

        /// <summary>
        /// Parse all triggers in the world map.
        /// </summary>
        void ParseTriggers()
        {
            List<Primitive3D> removeList = new List<Primitive3D>();

            foreach (Primitive3D prim in primitiveList)
            {
                if (prim.name.ToLower().Equals("trigger"))
                {
                    triggers.Add(new Trigger(0, prim.BBox));
                    removeList.Add(prim);
                }
            }

            foreach (Primitive3D removal in removeList)
            {
                primitiveList.Remove(removal);
            }
        }

        /// <summary>
        /// Parse all world items in the world map.
        /// </summary>
        void ParseWorldItems()
        {
            Vector3 itemCenter;
            int itemType;
            List<Primitive3D> removeList = new List<Primitive3D>();

            foreach (Primitive3D prim in primitiveList)
            {
                if (prim.name.ToLower().Equals("item"))
                {
                    itemType = WorldItem.ConvertStringToWorldItemType(prim.userInfo.ToLower());

                    if (itemType != WorldConstants.ITEM_UNKNOWN)
                    {
                        itemCenter = Primitive3D.CalculateCenter(prim);
                        items.Add(new WorldItem(itemType, itemCenter));
                        removeList.Add(prim);
                    }
                }
            }

            foreach (Primitive3D removal in removeList)
            {
                primitiveList.Remove(removal);
            }
        }

        void ParsePlayerSpawn()
        {
            Primitive3D playerSpawn = null;

            foreach (Primitive3D prim in primitiveList)
            {
                if (prim.name.ToLower().Equals("p_spawn"))
                {
                    playerSpawn = prim;
                    playerSpawnLocation = Primitive3D.CalculateCenter(prim);
                    break;
                }
            }

            if (playerSpawn != null)
            {
                primitiveList.Remove(playerSpawn);
            }
        }

        void ParseZombieSpawns()
        {
            List<Primitive3D> removeList = new List<Primitive3D>();

            foreach (Primitive3D prim in primitiveList)
            {
                if (prim.name.ToLower().Equals("z_spawn"))
                {
                    zombieSpawnLocations.Add(Primitive3D.CalculateCenter(prim));
                    removeList.Add(prim);
                }
            }

            foreach (Primitive3D removal in removeList)
            {
                primitiveList.Remove(removal);
            }
        }

        public void ParseCustomContent()
        {
            ParseTriggers();
            ParseWorldItems();
            ParsePlayerSpawn();
            ParseZombieSpawns();
        }

        /// <summary>
        /// The main entry point for calling the operations of the Parser
        /// </summary>
        /// <param name="xmldocument"></param>
        public void ParseMapData(XmlDocument xmldocument)
        {
            //ReadMaterials(xmldocument);
            //ReadPrimitives(xmldocument);
            //ParseCustomContent();

            ParseXMLFile(xmldocument);
        }

        #region Custom XML Editor Methods
        void ParseXMLFile(XmlDocument xmldocument)
        {
            XmlNodeList wallList = xmldocument.GetElementsByTagName("wall");
            XmlNodeList floorList = xmldocument.GetElementsByTagName("floor");
            XmlNodeList spawnList = xmldocument.GetElementsByTagName("spawn");
            XmlNodeList particleList = xmldocument.GetElementsByTagName("particle");

            ParseWallsXml(wallList);
            ParseFloorsXML(floorList);
            ParseSpawnXML(spawnList);
            ParseParticleEmmittersXML(particleList);
        }

        void ParseWallsXml(XmlNodeList wallList)
        {
            for (int i = 0; i < wallList.Count; i++)
            {
                String textureName = "";
                Vector3[] verts = new Vector3[4];
                Vector2[] coords = new Vector2[4];
                XmlNode wallNode = wallList.Item(i);
                XmlNodeList wallDetailNodes = wallNode.ChildNodes;

                int vertexIndex = 0;
                for (int child = 0; child < wallDetailNodes.Count; child++)
                {
                    XmlNode wallDetail = wallDetailNodes.Item(child);

                    if (wallDetail.Name.Equals("texture"))
                    {
                        textureName = wallDetail.InnerText;
                    }
                    else if (wallDetail.Name.Equals("vertex"))
                    {
                        Vector3 vertex = new Vector3();
                        Vector2 coord = new Vector2();
                        XmlAttributeCollection attributes = wallDetail.Attributes;

                        vertex.X = float.Parse(attributes.Item(0).Value);
                        vertex.Y = float.Parse(attributes.Item(1).Value);
                        vertex.Z = float.Parse(attributes.Item(2).Value);

                        coord.X = float.Parse(attributes.Item(3).Value);
                        coord.Y = float.Parse(attributes.Item(4).Value);

                        verts[vertexIndex] = vertex;
                        coords[vertexIndex] = coord;

                        vertexIndex++;

                    }
                }

                //Create the primitive now
                Primitive3D cPrimitive = new Primitive3D();
                cPrimitive.name = textureName;
                TexturedPolygon tPolygon = new TexturedPolygon();
                for (int v = 0; v < verts.Length; v++)
                {
                    tPolygon.AddTexturedVertex(verts[v], coords[v].X, coords[v].Y);
                }
                tPolygon.CalculateBoundingBox();

                cPrimitive.AddPolygon(tPolygon);
                cPrimitive.CalculateBoundingBox();
                primitiveList.Add(cPrimitive);
                
            }
        }

        void ParseFloorsXML(XmlNodeList floorList)
        {
            for (int i = 0; i < floorList.Count; i++)
            {
                String textureName = "";
                Vector3[] verts = new Vector3[4];
                Vector2[] coords = new Vector2[4];
                XmlNode wallNode = floorList.Item(i);
                XmlNodeList wallDetailNodes = wallNode.ChildNodes;

                int vertexIndex = 0;
                for (int child = 0; child < wallDetailNodes.Count; child++)
                {
                    XmlNode wallDetail = wallDetailNodes.Item(child);

                    if (wallDetail.Name.Equals("texture"))
                    {
                        textureName = wallDetail.InnerText;
                    }
                    else if (wallDetail.Name.Equals("vertex"))
                    {
                        Vector3 vertex = new Vector3();
                        Vector2 coord = new Vector2();
                        XmlAttributeCollection attributes = wallDetail.Attributes;

                        vertex.X = float.Parse(attributes.Item(0).Value);
                        vertex.Y = float.Parse(attributes.Item(1).Value);
                        vertex.Z = float.Parse(attributes.Item(2).Value);

                        coord.X = float.Parse(attributes.Item(3).Value);
                        coord.Y = float.Parse(attributes.Item(4).Value);

                        verts[vertexIndex] = vertex;
                        coords[vertexIndex] = coord;

                        vertexIndex++;

                    }
                }

                //Create the primitive now
                Primitive3D cPrimitive = new Primitive3D();
                cPrimitive.name = textureName;
                TexturedPolygon tPolygon = new TexturedPolygon();
                for (int v = 0; v < verts.Length; v++)
                {
                    tPolygon.AddTexturedVertex(verts[v], coords[v].X, coords[v].Y);
                }
                tPolygon.CalculateBoundingBox();

                cPrimitive.AddPolygon(tPolygon);
                cPrimitive.CalculateBoundingBox();
                primitiveList.Add(cPrimitive);

            }
        }

        void ParseSpawnXML(XmlNodeList spawnList)
        {
            //Set to some default location
            playerSpawnLocation = Vector3.Zero;

            for (int i = 0; i < spawnList.Count; i++)
            {
                String spawnType = "";
                Vector3 location = Vector3.Zero;

                XmlNode spawnNode = spawnList.Item(i);
                XmlNodeList spawnDetailsNode = spawnNode.ChildNodes;

                for (int child = 0; child < spawnDetailsNode.Count; child++)
                {
                    XmlNode childNode = spawnDetailsNode.Item(child);

                    if (childNode.Name.Equals("type"))
                    {
                        spawnType = childNode.InnerText;
                    }
                    else if (childNode.Name.Equals("position"))
                    {
                        XmlAttributeCollection attributes = childNode.Attributes;
                        location.X = float.Parse(attributes.Item(0).Value);
                        location.Y = float.Parse(attributes.Item(1).Value);
                        location.Z = float.Parse(attributes.Item(2).Value);
                    }
                }

                //What type of spawn are we dealing with?
                if (spawnType.Equals("player"))
                {
                    playerSpawnLocation = location;
                }
                else if (spawnType.Equals("zombie"))
                {
                    zombieSpawnLocations.Add(location);
                }
            }
        }

        void ParseParticleEmmittersXML(XmlNodeList particleList)
        {
            for (int i = 0; i < particleList.Count; i++)
            {
                String particleType = "";
                Vector3 location = Vector3.Zero;
                Vector3 direction = Vector3.Up;

                XmlNode particleNode = particleList.Item(i);
                XmlNodeList particleDetailNodes = particleNode.ChildNodes;

                for (int child = 0; child < particleDetailNodes.Count; child++)
                {
                    XmlNode childNode = particleDetailNodes.Item(child);

                    if (childNode.Name.Equals("type"))
                    {
                        particleType = childNode.InnerText;
                    }
                    else if (childNode.Name.Equals("position"))
                    {
                        XmlAttributeCollection attributes = childNode.Attributes;
                        location.X = float.Parse(attributes.Item(0).Value);
                        location.Y = float.Parse(attributes.Item(1).Value);
                        location.Z = float.Parse(attributes.Item(2).Value);
                    }
                    else if (childNode.Name.Equals("direction"))
                    {
                        XmlAttributeCollection attributes = childNode.Attributes;
                        direction.X = float.Parse(attributes.Item(0).Value);
                        direction.Y = float.Parse(attributes.Item(1).Value);
                        direction.Z = float.Parse(attributes.Item(2).Value);
                    }
                }
                
                //What type of particle system are we dealing with?
                if (particleType.Equals("smoke"))
                {
                    StaticParticleEmitter smokeEmitter = new StaticParticleEmitter(ZEngine.smokeParticleSystem, location, direction * 10f);
                    staticEmitters.Add(smokeEmitter);
                }

            }
        }
        #endregion
    }
}
